#!/usr/bin/python3
# -*- coding: utf-8 -*
"""
Description: configuration read utils.
Class: IniConfig.
"""

import xml
import json
import codecs
import configparser
from configparser import NoOptionError, NoSectionError, ParsingError
import yaml
import xmltodict

from .validate import validate_path
from .time_utils import time_limit
from .log import LOGGER

TIMEOUT = 1


class IniConfig():
    """
    Ini configuration reader.
    """

    def __init__(self, config_path=None):
        """
        class instance initialization.

        Args:
            config_path (str): configuration path.
        """
        self.conf = None

        if validate_path(config_path):
            try:
                self.conf = configparser.RawConfigParser()
                self.conf.read(config_path, encoding='utf-8')
            except ParsingError as error:
                LOGGER.error(error)
        else:
            LOGGER.error("Please check the file: %s.", config_path)

    def update(self, config_path):
        """
        configuration update.

        Args:
            config_path (str): configuration path.
        """
        self.__init__(config_path)

    def get_sections(self):
        """
        get sections of the ini configuration.

        Returns:
            list: sections.
        """
        if self.conf is not None:
            return self.conf.sections()

        return []

    def get_options(self, sect):
        """
        get all options in specified section.

        Args:
            sect (str): get options from which section.

        Returns:
            list: options of the specified section.
        """
        if sect not in self.get_sections():
            LOGGER.error(
                "There is no such a section: %s, please check again.", sect)
            return None
        return self.conf.options(sect)

    def get_value(self, sect, param):
        """
        get value of param in sect.

        Args:
            sect (str): in which section.
            param (str): parameter.

        Returns:
            str: value
        """
        if not all([sect, param]) or self.conf is None:
            return None
        try:
            return self.conf.get(sect, param)
        except NoSectionError:
            LOGGER.error(
                "There is no such a section: %s, please check again.", sect)
            return None
        except NoOptionError:
            LOGGER.error(
                "There is no such a parameter '%s' in '%s'", param, sect)
            return None

    def get_section_values(self, sect):
        """
        get params in section.

        Args:
            sect (str): in which section.

        Returns:
            dict: params
        """
        result = dict()
        options = self.get_options(sect)

        if options is not None:
            for option in options:
                result[option] = self.get_value(sect, option)

        return result


def read_yaml_config_file(config_file):
    """
    Read yaml configuration file.

    Args:
        config_file (str): the configuration file path

    Returns:
        dict/None
    """
    conf = None
    if config_file is None:
        return conf

    if validate_path(config_file):
        try:
            with open(config_file, 'r') as file_io:
                conf = yaml.safe_load(file_io.read())
                if not isinstance(conf, (dict, list)):
                    LOGGER.error(
                        "YAML [%s] didn't produce a dictionary or list.", config_file)
                    conf = None
        except yaml.scanner.ScannerError:
            LOGGER.error("Couldn't parse yaml %s ", config_file)

    return conf


def read_json_config_file(config_file):
    """
    Read json configuration file.

    Args:
        config_file (str): the configuration file path

    Returns:
        dict/None
    """
    conf = None
    if config_file is None:
        return conf

    if validate_path(config_file):
        try:
            with open(config_file, 'r') as file_io:
                conf = json.load(file_io)
        except json.decoder.JSONDecodeError as error:
            LOGGER.error('%s in %s', str(error), config_file)

    return conf


def read_xml_config_file(config_file):
    """
    Read xml configuration file.

    Args:
        config_file (str): the configuration file path

    Returns:
        dict/None
    """
    conf = None
    if config_file is None:
        return conf

    if validate_path(config_file):
        try:
            with open(config_file, 'r') as file_io:
                xml_parse = xmltodict.parse(file_io.read())
                conf = json.loads(json.dumps(xml_parse))
        except xml.parsers.expat.ExpatError:
            LOGGER.error('%s parsed failed.', config_file)

    return conf


@time_limit(TIMEOUT)
def code_read(file_path, encode):
    """
    file read for python2 with encoding.

    Args:
        file_path (str): file path.
        encode (str): encode

    Returns:
        list
    """
    data = []
    with codecs.open(file_path, 'r', encoding=encode, errors='ignore') as file_io:
        line = file_io.readline()
        while line:
            if "No such file or directory" in line:
                break
            data.append(line)
            line = file_io.readline()

    return data
